﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace RayTracer
{
    /// <summary>
    /// Control-Klasse zum Anzeigen der Ausgabe.
    /// </summary>
    public partial class ViewPort : System.Windows.Forms.PictureBox
    {
        #region Konstanten
        private const int MAX_THREAD_MEMORY = 32 * 1024 * 1024; // Bytes
        #endregion Konstanten

        #region Attribute
        private static object syncLock = new object();
        private static int ID = 1;
        private int id;
        private RayTracer rayTracer;
        private bool showFPS = false;
        private bool autoResizeRaytracerResolution = true;
        private Thread thread;
        private DateTime fpsLastFrameTime = DateTime.Now;
        private float fps;
        private object paintSync = new object();
        #endregion Attribute

        #region Eigenschaften
        /// <summary>
        /// Gibt und setzt den verwendeten Raytracer.
        /// </summary>
        public RayTracer RayTracer
        {
            get { return rayTracer; }
            set
            {
                rayTracer = value;
                if (value != null)
                {
                    rayTracer.Started += new global::RayTracer.RayTracer.SimpleEvent(rayTracer_Started);
                    rayTracer.Stopped += new global::RayTracer.RayTracer.SimpleEvent(rayTracer_Stopped);
                    rayTracer.Resolution = Size;
                }
                if (thread != null) { try { thread.Abort(); } catch { } }
                thread = null;
            }
        }

        /// <summary>
        /// Gibt und setzt den Indikator, ob die FPS angezeigt werden sollen.
        /// </summary>
        public bool ShowFPS
        {
            get { return showFPS; }
            set { showFPS = value; }
        }

        /// <summary>
        /// Gibt und setzt den Indikator, ob die Auflösung des Raytracers automatisch angepasst werden soll.
        /// </summary>
        public bool AutoResizeRaytracerResolution { get { return autoResizeRaytracerResolution; } set { autoResizeRaytracerResolution = value; } }

        /// <summary>
        /// Gibt den Mittelpunkt des Controls auf dem Bildschirm an.
        /// </summary>
        public System.Drawing.Point CenterScreenPoint { get { return PointToScreen(new System.Drawing.Point(Width / 2, Height / 2)); } }

        /// <summary>
        /// Eine Kopie der aktuellen Frame.
        /// </summary>
        public Bitmap CurrentPicture { get { lock (paintSync) { return new Bitmap(Image); } } }
        #endregion Eigenschaften

        #region Methoden
        #region Konstruktoren
        /// <summary>
        /// Initiiert und intialisert die Klasse.
        /// </summary>
        public ViewPort()
        {
            lock (syncLock) id = ID++;

            InitializeComponent();
            base.BackColor = Color.White;
        }
        #endregion Konstruktoren

        #region Raytracer Handling
        /// <summary>
        /// Startet die Laufzeit wenn der Raytracer gestartet wurde.
        /// </summary>
        /// <param name="sender">Der Raytracer.</param>
        void rayTracer_Started(RayTracer sender)
        {
            if (sender == rayTracer)
            {
                rayTracer_Stopped(sender);
                thread = new Thread(new ThreadStart(rayTracer_Handler), MAX_THREAD_MEMORY);
                thread.Name = "Raytracer ViewPort " + id;
                thread.Start();
            }
        }

        /// <summary>
        /// Behandelt den Raytracer.
        /// </summary>
        void rayTracer_Handler()
        {
            try
            {
                while (rayTracer.State == global::RayTracer.RayTracer.RayTracerState.Running)
                {
                    try
                    {
                        Bitmap newImage = rayTracer.GetNextImage();
                        DateTime newFrameTime = DateTime.Now;
                        fps = (float)1000 / (float)((new TimeSpan(newFrameTime.Ticks - fpsLastFrameTime.Ticks)).TotalMilliseconds);
                        fpsLastFrameTime = newFrameTime;
                        lock (paintSync)
                        {
                            Image = newImage;
                            // Das alte Bild darf hier nicht Disposed werden, da es noch genutzt wird.
                        }
                    }
                    catch { }
                }
            }
            catch { }
        }

        /// <summary>
        /// Stoppt die Laufzeit wenn der Raytracer beendet wurde.
        /// </summary>
        /// <param name="sender">Der Raytracer.</param>
        void rayTracer_Stopped(RayTracer sender)
        {
            if (sender == rayTracer)
            {
                if (thread != null) { try { thread.Abort(); } catch { } }
                thread = null;
            }
        }
        #endregion Raytracer Handling

        /// <summary>
        /// Führt die eigentliche Zeichnung des Graphen durch.
        /// </summary>
        /// <param name="pe"></param>
        protected override void OnPaint(PaintEventArgs pe)
        {
            lock (paintSync)
            {
                base.OnPaint(pe);
                if (showFPS)
                {
                    pe.Graphics.WriteHUDText(new PointF(5, 5), "FPS: " + Math.Round(fps, 1).ToString(), Color.Gold, Color.Black);
                    /*      pe.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(64, 0, 0, 0)), new Rectangle(3, 3, (int)((4 + (Math.Round(fps, 1).ToString().Length)) * 10.5f) + 2, 18 + 2));
                          pe.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(92, 0, 0, 0)), new Rectangle(4, 4, (int)((4 + (Math.Round(fps, 1).ToString().Length)) * 10.5f) - 1, 18 - 1));
                          pe.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(128, 0, 0, 0)), new Rectangle(5, 5, (int)((4 + (Math.Round(fps, 1).ToString().Length)) * 10.5f), 18));
                          pe.Graphics.DrawString("FPS: " + Math.Round(fps, 1).ToString(), new Font("Courier New", 10, FontStyle.Bold), Brushes.Gold, new PointF(5, 5));*/
                }
            }
        }

        /// <summary>
        /// Wird bei einer regulären Größenänderung ausgeführt.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (autoResizeRaytracerResolution && rayTracer != null) rayTracer.Resolution = Size.Height < 32 || Size.Width < 32 ? new Size(32, 32) : Size;
        }
        #endregion Methoden
    }
}
